//$Id: aiTestCase.java 248 2010-11-12 15:11:32Z liangyj20030161@yahoo.com.sg $
package builders;

import java.util.ArrayList;

/**
 * This aiTestCase does the *real* work for UnitTestingAI. It takes in test cases from UnitTestingAI,
 * computes and does comparison between the output and expected output.
 * @author Liang Yujing
 *
 */
public class aiTestCase {
	private YujingAI ai;
	private String myCommand;
	private String expectedOutput;
	private String result;
	private boolean correct = false;
	private int NUM_OF_RESOURCES = 6;
	private int RESOURCE_AND_REQ = 3;
	private int SET_IN_EACH_RESOURCE = 2;
	private GameState gameState;
	private ArrayList<Integer> gameStateTilesYouCanMoveTo;
	private ArrayList<Building> gameStateBuildingsYouCanBuild = new ArrayList<Building>();
	private ArrayList<Building> gameStateAllBuildingsAvailableForBuilding = new ArrayList<Building>();
	private int dieValue;
	private String[] playerCurrentResourceAmount;
	private enum command{
		SMART_BUILDING, STUPID_BUILDING, SMART_TILE, STUPID_TILE
	}
	
	/**
	 * Constructor for aiTestCase
	 * @param String tilesYouCanMoveTo
	 * @param String buildingsYouCanBuild
	 * @param String building1
	 * @param String building2
	 * @param String building3
	 * @param String building4
	 * @param String ieValue
	 * @param String currentResourceAmount
	 * @param String command
	 * @param String expectedOutput
	 */
	public aiTestCase(String tilesYouCanMoveTo, String buildingsYouCanBuild, String building1, String building2, String building3, String building4, String dieValue, String currentResourceAmount, String command, String expectedOutput) {
		this.myCommand = command;
		this.expectedOutput = expectedOutput;
		gameStateTilesYouCanMoveTo = stringArrayToArrayListInteger(split(tilesYouCanMoveTo));
		addBuilding(gameStateAllBuildingsAvailableForBuilding,(createBuilding(split(building1))));
		addBuilding(gameStateAllBuildingsAvailableForBuilding,(createBuilding(split(building2))));
		addBuilding(gameStateAllBuildingsAvailableForBuilding,(createBuilding(split(building3))));
		addBuilding(gameStateAllBuildingsAvailableForBuilding,(createBuilding(split(building4))));
		createBuildingsYouCanBuild(split(buildingsYouCanBuild));
		playerCurrentResourceAmount = split(currentResourceAmount);
		if (isInteger(dieValue)){
			this.dieValue = Integer.parseInt(dieValue);
		}
	}

	/**
	 * This part verifies if the output computed is the same as the expected output.
	 * It sets the boolean correct to true if output matches the expected output.
	 */
	public void verifyResult() {
		result = this.checkResults();
		if (result == null){
			if (expectedOutput.equals("null")){
				correct = true;
			}
		} else if (result.equals(expectedOutput)){
			correct = true;
		}
	}

	/**
	 * This part returns the boolean correct to UnitTestingAI.
	 * @return boolean correct
	 */
	public boolean getResult() {
		return correct;
	}

	/**
	 * This part returns the String result, and is only used if the output differs from the expected output.
	 * @return String result
	 */
	public String getOuput() {
		return result;
	}

	/**
	 * This part returns the String expected output.
	 * It is only used if the output differs from the expected output.
	 * @return String expectedOutput
	 */
	public String getExpectedOutput() {
		return expectedOutput;
	}
	
	/**
	 * Just like add method for ArrayList, except that it does not add if input is null
	 * @param allBuildingsAvailableForBuilding
	 * @param building
	 */
	private void addBuilding( ArrayList<Building> allBuildingsAvailableForBuilding,	Building building) {
		if (building != null){
			allBuildingsAvailableForBuilding.add(building);
		}
	}

	/**
	 * This part sets the gameState to the values that are inputed in from the test cases,
	 * and does the computation to derive the output. It sets the String result to the output derived.
	 * Note that 0 used to created the AI is a dummy value, and is not used at all
	 * @return String result
	 */
	private String checkResults(){		
		ai = new YujingAI("dummy", 0);
		gameState = new GameState();
		
		gameState.setTileManager(new TileManager("defaultTile.txt"));
		gameState.setBuildingsYouCanBuild(gameStateBuildingsYouCanBuild);
		gameState.setAllBuildingsAvailableForBuilding(gameStateAllBuildingsAvailableForBuilding);
		gameState.setMovementActivityPoints(dieValue);
		gameState.setTilesYouCanMoveTo(gameStateTilesYouCanMoveTo);		
		ai.setCurrentResourcesList(convertCurrentResourcesList(playerCurrentResourceAmount));
			
		switch (convertCommandToInt(myCommand)) {
		case SMART_BUILDING: {
			if (ai.chooseBuildingThinkSmart(gameState) == null) {
				return null;
			} else
				return ai.chooseBuildingThinkSmart(gameState).getName();
		}
		case STUPID_BUILDING:
			return ai.chooseBuildingThinkStupid(gameState);
		case SMART_TILE:
			return ai.chooseTileThinkSmart(gameState) + "";
		case STUPID_TILE:
			return ai.chooseTileThinkStupid(gameState) + "";
		default:
			return "error";
		}
	}
		
	/**
	 * Tests if a String is an Integer
	 * @param string
	 * @return boolean
	 */
	private boolean isInteger(String string) {
		try {
			Integer.valueOf(string);
			return true;
		} catch (NumberFormatException e) {
			return false;
		}
	}
	
	/**
	 * Splits a String into a String[] based on white spaces in between words in the String
	 * @param input
	 * @return String[]
	 */
	private String[] split(String input){
		String delimiter = " ";
		if (input != null){
			return input.split(delimiter);
		} else return null;
	}
	
	/**
	 * Converts a String[] into an ArrayList<Integer>
	 * @param array
	 * @return ArrayList<Integer>
	 */
	private ArrayList<Integer> stringArrayToArrayListInteger(String[] array){
		ArrayList<Integer> result = new ArrayList<Integer>();
		for (String s : array){
			if (isInteger(s)){
				result.add(Integer.parseInt(s));
			}
		}
		return result;
	}
	
	/**
	 * Converts a String[] of Building names and building requirements into a Building
	 * @param building
	 * @return Building
	 */
	private Building createBuilding(String[] building){
		if (building[0].equals("*")){
			return null;
		}
		
		int numOfResources = (building.length - RESOURCE_AND_REQ) / SET_IN_EACH_RESOURCE;		
		BuildingRequirement[] req = new BuildingRequirement[numOfResources];
		int reqIndex = 0;
		String name = building[0];
		String points = building[1];
		String quota = building[2];

		for (int i = 3; i < building.length; i = i+2){
			Player.Resource resource = convertStringToResource(building[i]);
			int amount = Integer.parseInt(building[i+1]);
				
			BuildingRequirement requirement = new BuildingRequirement(resource, amount);
			req[reqIndex++] = requirement;
		}

		return new Building(name, "dummy", points, quota, req);
	}
	
	/**
	 * Based on gameStateAllBuildingsAvailableforBuilding, adds Buildings that AI can build into
	 * gameStateBuildingsYouCanBuild
	 * @param buildingsArray
	 */
	private void createBuildingsYouCanBuild(String[] buildingsArray){
		for (int i = 0 ; i < buildingsArray.length ; i++){
			for (Building b : gameStateAllBuildingsAvailableForBuilding){
				if (b.getName().equals(buildingsArray[i])){
					gameStateBuildingsYouCanBuild.add(b);
				}
			}
		}
	}
	
	/**
	 * Converts String[] into an int[]
	 * @param currentResources
	 * @return int[]
	 */
	private int[] convertCurrentResourcesList(String[] currentResources){
		int[] result = new int[NUM_OF_RESOURCES];
		int index = 0;
		for (String s : currentResources){
			if (isInteger(s)){
				int i = Integer.parseInt(s);
				result[index] = i;
			}
			index++;
		}
		return result;
	}
	
	/**
	 * Converts a String into an enum command
	 * @param command
	 * @return enum command
	 */
	private command convertCommandToInt(String command){
		if (command.equals("SMART_BUILDING")){
			return aiTestCase.command.SMART_BUILDING;
		} else if (command.equals("STUPID_BUILDING")){
			return aiTestCase.command.STUPID_BUILDING;
		} else if (command.equals("SMART_TILE")){
			return aiTestCase.command.SMART_TILE;
		} else if (command.equals("STUPID_TILE")){
			return aiTestCase.command.STUPID_TILE;
		} else return null;
	}
	
	/**
	 * Converts a String into an enum Player.Resource
	 * @param resource
	 * @return enum Player.Resource
	 */
	private Player.Resource convertStringToResource(String resource){
		if (resource.equals("cement")){
			return Player.Resource.CEMENT;
		} else if (resource.equals("brick")){
			return Player.Resource.BRICK;
		} else if (resource.equals("mud")){
			return Player.Resource.MUD;
		} else if (resource.equals("glass")){
			return Player.Resource.GLASS;
		} else if (resource.equals("dung")){
			return Player.Resource.DUNG;
		} else if (resource.equals("sand")){
			return Player.Resource.SAND;
		} else return null;
	}
}
